package day7.timer.impl;

import java.util.concurrent.PriorityBlockingQueue;

public class MyTimer {
    // 存放延时任务的优先级队列
    private final PriorityBlockingQueue<MyTimerTask> queue = new PriorityBlockingQueue<>();
    // 定义一个锁
    private final Object newTaskComing = new Object();

    public MyTimer() {
        // 开始实施任务
        Worker worker = new Worker();
        worker.start();
    }
    // 定时器的启动方法
    public void schedule(MyTimerTask task, long delay) {
        // 该方法非工作线程调用
        task.runAt = System.currentTimeMillis() + delay;
        queue.put(task);
        synchronized (newTaskComing) {
            // 唤醒优先级最高的任务
            newTaskComing.notify();
        }
    }

    // 实际上实施任务的线程类
    class Worker extends Thread {

        @Override
        public void run() {
            while (true) {
                // 先从队列中获取任务
                MyTimerTask task = null;
                try {
                    task = queue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // task 应该有个应该执行的时刻（不能记录 delay）
                long now = System.currentTimeMillis();
                long delay = task.runAt - now;
                if (delay <= 0) {
                    task.run();
                } else {
                    try {
                        // 应该在两种条件下醒来：
                        // 1. 有新的任务过来了（任务可能比当前最小的任务更靠前）
                        // 2. 没有新任务来，但到了该执行该任务的时候了
                        synchronized (newTaskComing) {
                            newTaskComing.wait(delay);
                        }

                        // 如果当前时间已经在要执行任务的时间之后了
                        // 说明任务的执行时间已过，所以应该去执行任务了
                        // 否则，先把这个任务放回去（因为时间还没到），再去取最小的任务
                        if (System.currentTimeMillis() >= task.runAt) {
                            task.run();
                        } else {
                            queue.put(task);
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
